/**	@file operators.cpp*/
#include "operators.h"
#include "ComparerIx.h"
#include "Add.h"
#include "Mul.h"
#include "Pow.h"
#include "Number.h"
#include "Expr.h"
namespace sympycpp {
Ex * Operations::addition(const Ex * L, const Ex * R) {
	Ex * L1 = L->copy();
	Ex * R1 = R->copy();
	return addition(L1, R1, STEALING);
}
Ex * Operations::subtraction(const Ex * L, const Ex * R) {
	Ex * L1 = L->copy();
	Ex * R1 = R->copy();
	R1->sign(!R1->sign());
	return addition(L1, R1, STEALING);
}
Ex * Operations::subtraction(Ex * & L, Ex * & R, allocationPolicy flag) {
	R->sign(!R->sign());
	return addition(L, R, STEALING);
}
Ex * Operations::addition(Ex * & L, Ex * & R, allocationPolicy flag) {
	basic_container v;// results
	basic_container newArgs_L; // if L is not addition then it is stored here (simulation of sequence of summands)
	basic_container newArgs_R; // if R is ...		----------------  ||  -----------------
	basic_container * summands_L; // pointer for manipulation with addends from L 
	basic_container * summands_R; // 	--------  || -------		    R
	if (L->isAdd()) { // first sequence for merging
		summands_L = &(((Add *)L)->args_);
	}
	else{
		newArgs_L.push_back(L);
		L = 0;
		summands_L = &newArgs_L;
	}
	if (R->isAdd()){// second sequence for merging
		summands_R = &(((Add *)R)->args_);
	}
	else{
		newArgs_R.push_back(R);
		R = 0;
		summands_R = &newArgs_R;

	}
	iterator li = summands_L->begin();
	iterator ri = summands_R->begin();
	while (li != summands_L->end()  && ri != summands_R->end()) { //merging
		Ex * L_summand = *li;
		Ex * R_summand = *ri;
		*li = 0;
		*ri = 0;

		if (L_summand->isNum() && (((Number *)L_summand)->eq(0))) {	//if element is 0 then  skip it
			delete L_summand;
			*ri = R_summand;
			++li;
			continue;
		}
		if (R_summand->isNum() && (((Number *)R_summand)->eq(0))) {	//if element  is 0 then skip it 
			delete R_summand;
			*li = L_summand;
			++ri;
			continue;
		}
		switch (ComparerIx::compatibilityForAddition(L_summand, R_summand)) {		//detection of Addend's relation 
			case INCOMPATIBLE : {// A B are different, sum is not possible
				if (ComparerIx::addLessThan(L_summand, R_summand)) { // less addend put in container of new addends
					v.push_back(L_summand);			     // and greater addend insert back
					*ri = R_summand;
					++li;
				}
				else{
					v.push_back(R_summand);
					*li = L_summand;
					++ri;
				}
				break;
			}
			case MULTIPLE_MULTIPLE : {		//Number*A*B Number*A*B	
				Number sum_Multiplicity(0); 
				sum_Multiplicity.setToAddition(L_summand->Multiplicity(), R_summand->Multiplicity());
				if (!sum_Multiplicity.eq(0)) {
					if (sum_Multiplicity.eq(1) || sum_Multiplicity.eq(-1)) {
						iterator it = ((Mul *)L_summand)->args_.begin();
						++it;
						Ex * newSummand = *it;
						newSummand->sign(sum_Multiplicity.sign());
						(*it) = 0;
						delete R_summand;
						delete L_summand;
						v.push_back(newSummand);
					}
					else {
						Ex * Multiplicity = new Number(sum_Multiplicity);
						L_summand->sign(Multiplicity->sign()); //reusing of structure of left multiple addends
						Multiplicity->sign(plus);
						(*((Mul *)L_summand)->args_.begin()) = Multiplicity;
						v.push_back(L_summand);
						delete R_summand;
					}
				}
				else{
					delete L_summand;
					delete R_summand;
				}
				++li;++ri;
				break;
		   	}
			case PLAIN_MULTIPLE : {		//A*B Number*A*B
				Ex * temp = L_summand; 
				L_summand = R_summand;
				R_summand = temp;	// without break (here is only change of pointers)
			}
			case MULTIPLE_PLAIN : {//Number*A*B A*B
				Number sum_Multiplicity((R_summand->sign() == plus) ? 1 : -1);
				sum_Multiplicity.setToAddition(sum_Multiplicity, L_summand->Multiplicity());
				if (!sum_Multiplicity.eq(0)){  
					if (sum_Multiplicity.eq(1) || sum_Multiplicity.eq(-1)) {
						iterator it = ((Mul *)L_summand)->args_.begin();
						++it;
						Ex * newSummand = *it;
						newSummand->sign(sum_Multiplicity.sign());
						(*it) = 0;
						delete R_summand;
						delete L_summand;
						v.push_back(newSummand);
					}
					else {
						Ex * Multiplicity = new Number(sum_Multiplicity);
						L_summand->sign(Multiplicity->sign());
						Multiplicity->sign(plus);
						(*((Mul *)L_summand)->args_.begin()) = Multiplicity;
						v.push_back(L_summand);
						delete R_summand;
					}
				}
				else{
					delete L_summand;
					delete R_summand;
				}
				++li;++ri;
				break;
			}
			case NUMBER_NUMBER : {	//Number Number
				Number * L_number = (Number *)L_summand;
				L_number->setToAddition(*L_number, *(Number *)R_summand);
				if (!L_number->eq(0)){		
					v.push_back(L_summand);
					delete R_summand;
				}
				else{	
					delete L_summand;
					delete R_summand;

				}
				++li;++ri;
				break;
			}
			case PLAIN_PLAIN : {		//A	A
				if ( L_summand->sign() == R_summand->sign()) {
					Ex * tempTwo = new Number(2);
					v.push_back(multiplication(tempTwo, L_summand, STEALING));
					delete R_summand;
				}
				else{
					delete L_summand;
					delete R_summand;
				}
				++li;++ri;
				break;
				}	
			default:
				assert(false);
			}
				
	}
	while (summands_L->end() != li) {		//redundat Addends
		v.push_back(*li);
		*li = 0;
		++li;
	}
	while (summands_R->end() != ri) {		//redundat Addends
		v.push_back(*ri);
		*ri = 0;
		++ri;
	}
	delete L;
	delete R;		
	if (v.size() == 0)
		return new Number(0);
	if (v.size() == 1)
		return *v.begin();
	else
		return new Add(v, STEALING);  
}

Expr Operations::operatorAdd(const  Expr & L, const  Expr & R) {
	Ex * p = addition(L.e_, R.e_);
	Expr e(p, Expr::STEALING);
	return e;
}
Expr Operations::operatorSub(const Expr & L, const Expr & R) {
	Expr e(R);
	e.sign(!R.sign());
	Ex * p = addition(L.e_, e.e_);
	return Expr(p, Expr::STEALING);
}
Ex * Operations::multiplication(const Ex * LL, const Ex * RR) {
	Ex * L = LL->copy();
	Ex * R = RR->copy();
	Ex * e = multiplication(L, R, STEALING);
	return e;

}
Ex * Operations::multiplicationMultiples(Ex * & L, Ex * & R, const allocationPolicy flagOfSteal) {
	assert(L->isMultiple());
	assert(R->isMultiple());
	Mul * MulL = (Mul *)L;	
	Mul * MulR = (Mul *)R;	
	Ex * newMultiplicity = multiplication(*MulL->args_.begin(), *MulR->args_.begin(), STEALING);
	Ex *  LR = multiplication(*(++MulL->args_.begin()), *(++MulR->args_.begin()), STEALING);
	newMultiplicity->sign(L->sign() == R->sign());
	delete L;
	delete R;
	L = R = 0;
	return multiplication(newMultiplicity, LR, STEALING);
}
Ex * Operations::multiplicationOneMultiple(Ex * & L, Ex * & R, const allocationPolicy flagOfSteal) {
	assert(L->isMultiple());
	Mul * MulL = (Mul *)L;
	Ex * LR = multiplication(*(++MulL->args_.begin()), R, STEALING);
	if (LR->isMultiple()) { // number * (number*?) --> number*?
		 iterator li = MulL->args_.begin(); 
		 iterator lri = ((Mul*)LR)->args_.begin();
		 (*li)->sign(MulL->sign());	// sign is stored in Mul
		 (*lri)->sign(LR->sign());	// setting right sign for multiples
		 Ex * newMultiplicity = multiplication(*li, *lri, STEALING);
		 Ex * solution = multiplication(newMultiplicity, *(++lri), STEALING);
		 delete LR;
		 delete L;
		 L = R = 0;
		 return solution;
	}
	else {
		iterator li = MulL->args_.begin(); 
		(*li)->sign(MulL->sign()); // sign is stored in Mul that will be deleted
		Ex * solution = multiplication(*li, LR, STEALING);
		delete L;
		L = R = 0;
		return solution;
	}
}
Ex * Operations::multiplicationOneNumber(Ex * & L, Ex * & R, const allocationPolicy flagOfSteal) {
	assert(L->isNum());
	assert(!R->isMultiple());
	Number * valL = (Number *)L;
	if (valL->eq(0)) {
		delete R;
		L = R = 0;
		return valL;
	}
	if (valL->eq(1)) {
		delete L;
		Ex * temp = R;
		L = R = 0;
		return temp;
	}
	if (valL->eq(-1)) {
		delete L;
		Ex * temp = R;
		temp->sign(!temp->sign());
		L = R = 0;
		return temp;
	}
	if (R->isAdd() && ((Add *)R)->moreThan1Multiple()) {
		Add * aR = (Add *)R;
		Ex * sum = new Number(0);
		for (iterator i = aR->args_.begin(); i != aR->args_.end(); ++i) {
			Ex * cL = L->copy();
			Ex * tmp = Operations::multiplication(*i, cL, STEALING);
			sum = Operations::addition(sum, tmp, STEALING);
		}
		sum->sign(R->sign());
		delete R;
		delete L;
		L = R = 0;
		return sum;
	}
	else {
		return new Mul(L, R, STEALING);
	}
}
Ex * Operations::multiplication(Ex * & L, Ex * & R, const allocationPolicy flagOfSteal) {
	basic_container v; // results
	basic_container newArgs_L; // simulation of sequnce of multiplicands if L is not multiplication
	basic_container newArgs_R;//  simulation of sequnce of multiplicands if L is not multiplication
	basic_container * Multiplicands_L;
	basic_container * Multiplicands_R;
	Sign actualSign = plus;
	//special cases --> multiples and numbers
	if (L->isMultiple() && R->isMultiple()) { 
		return multiplicationMultiples(L, R, STEALING);
	}
	if (L->isMultiple()) {  
		return multiplicationOneMultiple(L, R, STEALING);
	}
	if (R->isMultiple()) {	 
		return multiplicationOneMultiple(R, L, STEALING);
	}
	if (L->isNum() && !R->isNum()) {
		return multiplicationOneNumber(L, R, STEALING);
	}
	if (!L->isNum() && R->isNum()) {
		return multiplicationOneNumber(R, L, STEALING);
	}
	if (L->isMul()) { // first sequnce for merging
		Multiplicands_L = & (((Mul *)L)->args_);//first vector consist of Multiplicands from L
		actualSign = L->sign();
	}
	else{
		newArgs_L.push_back(L);	// one element vector
		L = 0;
		Multiplicands_L = & newArgs_L;
	}
	if (R->isMul()) { //second sequence for merging
		Multiplicands_R = & (((Mul *)R)->args_);//second vector consist of elements from R
		actualSign = (actualSign == R->sign()) ? plus : minus;
	}
	else{
		newArgs_R.push_back(R);      //one element vector
		R = 0;
		Multiplicands_R = & newArgs_R;

	}
	iterator li = Multiplicands_L->begin();
	iterator ri = Multiplicands_R->begin();
	while ((Multiplicands_L->end() != li) && (Multiplicands_R->end() != ri)) { //merging
		Ex * L_Multiplicand = *li;
		Ex * R_Multiplicand = *ri;
		*li = 0;
		*ri = 0;

		if (L_Multiplicand->isNum() && (((Number *)L_Multiplicand)->eq(1))) {	//if element is 1 omit it 
			delete L_Multiplicand;
			*ri = R_Multiplicand;
			++li;
			continue;
		}
		if (R_Multiplicand->isNum() && (((Number*)R_Multiplicand)->eq(1))) {	//if element is 1  omit it 
			delete R_Multiplicand;
			*li = L_Multiplicand;
			++ri;
			continue;
		}
		switch (ComparerIx::compatibilityForMultiplication(L_Multiplicand, R_Multiplicand)) {	
 			case INCOMPATIBLE : {	//	A	B
				if(ComparerIx::mulLessThan(L_Multiplicand, R_Multiplicand)) {
					v.push_back(L_Multiplicand);
					++li;
					*ri = R_Multiplicand;
				}
				else{
					v.push_back(R_Multiplicand);
					++ri;
					*li = L_Multiplicand;
				}
				break;
			}
			case BASE_BASE : {		//X^(a)		 X^(b)
				Pow * L_power = (Pow *)L_Multiplicand;		
				Pow * R_power = (Pow *)R_Multiplicand;
				Ex * L_powerBase, * L_powerExp, * R_powerBase, * R_powerExp;			//auxiliary pointers
				L_powerBase = L_powerExp =  R_powerBase = R_powerExp = 0;
				if (L_power->sign() != R_power->sign()) {		//different  signs
					actualSign = !actualSign;		//change of sign of complete multiplication 
				}
				L_power->ganef(L_powerBase, L_powerExp, STEALING);
				R_power->ganef(R_powerBase, R_powerExp, STEALING);	
				Ex * newExp = addition(L_powerExp, R_powerExp, STEALING);	//new Exponent	
				Ex * newPow = power(L_powerBase, newExp, STEALING);
				if (newPow->isNum()) {
					if (!((Number *)newPow)->eq(1)) {
						v.push_back(newPow);
					}
					else{
						delete newPow;
					}

				}
				else {
					v.push_back(newPow);
				}
				delete L_power;
				delete R_power;
				delete R_powerBase;
				++li;++ri;
				break;
			}
			case PLAIN_BASE : {
				Ex * temp = L_Multiplicand;
				L_Multiplicand = R_Multiplicand;
				R_Multiplicand = temp;
			}
			case BASE_PLAIN : {		//X^(..)  X
				Pow * L_power = (Pow *)L_Multiplicand;
				Ex * L_powerBase, * L_powerExp, *I;
				L_powerBase =  L_powerExp = I = 0;
				I = new Number(1);
				if (L_Multiplicand->sign() != R_Multiplicand->sign()) {
					actualSign = !actualSign;
				}
				L_power->ganef(L_powerBase, L_powerExp, STEALING);
				delete R_Multiplicand;
				delete L_Multiplicand; // this deletes also L_power
				Ex * newExp = addition(L_powerExp, I, STEALING);
				Ex * newPow = power(L_powerBase, newExp, STEALING);	
				if (newPow->isNum()) {
					if (!((Number *)newPow)->eq(1)) {
						v.push_back(newPow);
					}
					else{
						delete newPow;
					}

				}
				else {
					v.push_back(newPow);
				}
				++li;++ri;
				break;
			
			}
			case NUMBER_NUMBER : {		//Number Number
				Number * L_Number = (Number *)L_Multiplicand;
				Number * R_Number = (Number *)R_Multiplicand;
				L_Number->setToMultiplication(*L_Number, * R_Number);
				v.push_back(L_Multiplicand);
				delete R_Multiplicand;
				++li;++ri;
				break;
			}
			case  PLAIN_PLAIN: {		//A A
				Number newExp = Number(2); //Number_pom.setValue(2);
				if (L_Multiplicand->sign() != R_Multiplicand->sign()) {
					actualSign = !actualSign;
				}
				v.push_back(power(L_Multiplicand, & newExp));
				delete L_Multiplicand;
				delete R_Multiplicand;
				++li;++ri;
				break;
			}
			default : 
				assert(false);
				break;
		}
	}
	while (Multiplicands_L->end() != li) { //redundant elements
		v.push_back(*li);
		*li = 0;
		++li;
	}
	while (Multiplicands_R->end() != ri) {//redundant elements
		v.push_back(*ri);
		*ri = 0;
		++ri;
	} 
	delete L;
	delete R;
	L = R = 0;
	if (v.size() == 0 ) {
		if (actualSign){
			return new Number(1);
		}
		else{
			return new Number(-1);
		}
	}
	iterator first = v.begin();
	if (actualSign == (*first)->sign()) {
		(*first)->sign(plus);
	}
	else {
		(*first)->sign(minus);
	}
	if (v.size() == 1) {
		return *first;
	}
	else { 
		return new Mul(v, STEALING);						
	}
}

Expr Operations::operatorMul(const  Expr & L, const Expr & R) {
	Ex * p = multiplication( L.e_, R.e_);
	Expr w(p, Expr::STEALING);
	return w;
}
Ex * Operations::releaseBothReturnFirst(Ex * & first, Ex * & second) {
	Ex * result = first;
	delete second;
	first = second = 0;
	return result;
}
Ex * Operations::division(const Ex * L, const Ex * R) {
	Ex * L1 = L->copy();
	Ex * R1 = R->copy();
	Ex * minOne = new Number(-1);
	Ex * invertedR = power(R1, minOne, STEALING);
	return multiplication(L1, invertedR, STEALING);
}
Ex * Operations::division(Ex * & L, Ex * & R, const allocationPolicy flagOfSteal) {
	Ex * minOne = new Number(-1);
	Ex * invertedR = power(R, minOne, STEALING);
	return multiplication(L, invertedR, STEALING);

}
Ex * Operations::power(const Ex * L, const Ex * R) {
	Ex * L1 = L->copy();
	Ex * R1 = R->copy();
	return power(L1, R1, STEALING);
}
Ex * Operations::power(Ex * & base, Ex * & exponent, const allocationPolicy flagOfSteal) {
	if (exponent->isNum()) { // x^Number
		Number * numberExponent = (Number *)exponent;
		if (numberExponent->eq(0)) { // x^0 => 1
			numberExponent->setValue(1);
			exponent->sign(plus);
			return releaseBothReturnFirst(exponent, base);		
		}	
		if (numberExponent->eq(1)) { // x^1 => x
			return releaseBothReturnFirst(base, exponent);
		}
	}
	if (base->isNum()) { // Number^x
		if (((Number *)base)->eq(1)) { // 1^x => 1
			return releaseBothReturnFirst(base, exponent);
		}
		if (((Number *)base)->eq(0)) { // 0^x => 0  BUG
			if (exponent->isNum() && exponent->sign() == minus) {
				throw ZeroDivision();
			}
			return releaseBothReturnFirst(base, exponent); // 0^x => 0
		}

	}

	if (base->isNum() && exponent->isNum()) { // Number^Integer => Number
		Number *  numBase = (Number *)base;
		Number *  numExponent = (Number *)exponent;
		if(numExponent->isInt()) {
			int exp = numExponent->getInt();
			numBase->setToPower(exp);
			return releaseBothReturnFirst(base, exponent);
		}
	}
	if ((base->sign() == minus) && (! base->isNum())) { // (-base)^exponent => (-1^exponent)*(base^exponent)
		base->sign(plus);
		Ex * minOne = new Number(-1);
		Ex * copyExponent = exponent->copy();
		Ex * signPower = power(minOne, copyExponent, STEALING); // (-1^exponent)
		Ex * mainPower= power(base, exponent, STEALING); // base^exponent
		return multiplication(signPower, mainPower, STEALING); // (-1^exponent)*(base^exponent)
	}
	if (base->isPow()) { // base^exponent == (x^y)^exponent => x^(y*exponent) 
		Pow * powBase = (Pow *)base;
		Ex * powBaseBase = 0;
		Ex * powBaseExponent = 0;
		powBase->ganef(powBaseBase, powBaseExponent, STEALING); // ganef makes invalid structure from powBase [actually base] => next step repair or delete it
		delete base; // also powBase is deleted
		base = 0 ;
		Ex * newExponent = 0;
		Ex * newPower = 0;
		newExponent = multiplication(powBaseExponent, exponent, STEALING); // y*exponent
		newPower  = power(powBaseBase, newExponent, STEALING); // x^(y*exponent)
		return  newPower;
	
	}
	if (base->isMul()) {// transform power that base is multiplication to multiplication that elements are powers
		Mul * mulBase = (Mul *)base;
		Ex * newPow = 0;
		Ex * old = 0;
		for (iterator i = mulBase->args_.begin(); i != mulBase->args_.end(); ++i) {
			Ex * exp = exponent->copy();
			newPow = power(*i,exp, STEALING);
			if (old != 0) {
				old = multiplication(newPow, old, STEALING);
			}
			else {
				old = newPow;
			}
		}
		delete base;
		delete exponent;
		base = exponent = 0;
		return old;
	}
	return new Pow(base, exponent, STEALING);
}
Expr Operations::operatorPow(const Expr & L, const Expr & R) {
	Ex * p = power(L.e_, R.e_);
	return Expr(p, Expr::STEALING);
}
Expr Operations::operatorDiv(const Expr & L,const Expr & R) {
	Number I(-1);
	Ex * p = power(R.e_, &I);
	Expr e(p, Expr::STEALING);
	p = multiplication(L.e_, e.e_);
	return Expr(p, Expr::STEALING);
}
}
